﻿//  Copyright © 2009-2010 by Rhy A. Mednick
//  All rights reserved.
//  http://rhyduino.codeplex.com
//  
//  Redistribution and use in source and binary forms, with or without modification, 
//  are permitted provided that the following conditions are met:
//  
//  * Redistributions of source code must retain the above copyright notice, this list 
//    of conditions and the following disclaimer.
//  
//  * Redistributions in binary form must reproduce the above copyright notice, this 
//    list of conditions and the following disclaimer in the documentation and/or other 
//    materials provided with the distribution.
//  
//  * Neither the name of Rhy A. Mednick nor the names of its contributors may be used 
//    to endorse or promote products derived from this software without specific prior 
//    written permission.
//  
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
//  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
//  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Rhyduino.Test
{
    ///<summary>
    ///  This is a test class for FirmataRequestMessageFormatterTest and is intended
    ///  to contain all FirmataRequestMessageFormatterTest Unit Tests
    ///</summary>
    [TestClass]
    public class FirmataEncoderTests
    {
        #region Public Methods

        ///<summary>
        ///  Tests the BuildAnalogWriteRequest method.
        ///</summary>
        [TestMethod]
        public void BuildAnalogWriteRequestTest()
        {
            const int value = 256;
            for (var pinNumber = 2; pinNumber < 13; pinNumber++)
            {
                var expected = new byte[] {(byte) (0xE0 | pinNumber), 0x00, 0x02};
                var actual = FirmataEncoder.BuildAnalogWriteRequest(pinNumber, value);

                actual.VerifyExpectedByValue(expected);
            }
        }

        ///<summary>
        ///  A test for BuildDigitalWriteRequest
        ///</summary>
        [TestMethod]
        public void BuildDigitalWriteRequestTest()
        {
            const int value = 0x7F;
            for (var portNumber = 0; portNumber < 2; portNumber++)
            {
                Console.Write("Testing port: ");
                Console.WriteLine(portNumber);

                // This evaluation for the expected value tests that we format the number 0x7F 
                // properly in a digital write request. For port 0 the first two bits are ignored
                // so the number is shifted and the value split into two parts: LSB = 0x03 and 
                // MSB = 0x7C. The two together equal 0x7F. For port 1, there's enough room to 
                // represent the value in one byte, so the LSB is 0x00.
                // TODO: Question: Do writes to port 0 ever use the last byte (the LSB) per the Firmata spec?
                var expected = (portNumber == 0)
                                   ? new[]
                                         {
                                             (byte) ((int) RequestMessageType.DigitalWrite | portNumber),
                                             (byte) 0x7C,
                                             (byte) 0x03
                                         }
                                   : new[]
                                         {
                                             (byte) ((int) RequestMessageType.DigitalWrite | portNumber),
                                             (byte) 0x7F,
                                             (byte) 0x00
                                         };

                var actual = FirmataEncoder.BuildDigitalWriteRequest(portNumber, value);

                actual.VerifyExpectedByValue(expected);
            }
        }

        ///<summary>
        ///  A test for BuildProtocolVersionRequest
        ///</summary>
        [TestMethod]
        public void BuildProtocolVersionRequestTest()
        {
            var expected = new[]
                               {
                                   (byte) (ResponseMessageType.SysexStart),
                                   (byte) (RequestMessageType.ProtocolVersion),
                                   (byte) (ResponseMessageType.SysexEnd)
                               };
            var actual = FirmataEncoder.BuildProtocolVersionRequest();

            actual.VerifyExpectedByValue(expected);
        }

        ///<summary>
        ///  A test for BuildReportAnalogPinRequest
        ///</summary>
        [TestMethod]
        public void BuildReportAnalogPinRequestTest()
        {
            for (var pinNumber = 0; pinNumber < 6; pinNumber++)
            {
                // First confirm the "turn on" messages
                var expected = new[]
                                   {
                                       (byte) ((int) RequestMessageType.ReportAnalogPin | pinNumber),
                                       (byte) 0x01
                                   };
                var actual = FirmataEncoder.BuildReportAnalogPinRequest(pinNumber, true);
                actual.VerifyExpectedByValue(expected);

                // And next, confirm the "turn off" messages
                expected = new[]
                               {
                                   (byte) ((int) RequestMessageType.ReportAnalogPin | pinNumber),
                                   (byte) 0x00
                               };
                actual = FirmataEncoder.BuildReportAnalogPinRequest(pinNumber, false);
                actual.VerifyExpectedByValue(expected);
            }
        }

        ///<summary>
        ///  A test for BuildReportDigitalPortRequest
        ///</summary>
        [TestMethod]
        public void BuildReportDigitalPortRequestTest()
        {
            for (var portNumber = 0; portNumber < 6; portNumber++)
            {
                // First confirm the "turn on" messages
                var expected = new[]
                                   {
                                       (byte) ((int) RequestMessageType.ReportDigitalPort | portNumber),
                                       (byte) 0x01
                                   };
                var actual = FirmataEncoder.BuildReportDigitalPortRequest(portNumber, true);
                actual.VerifyExpectedByValue(expected);

                // And next, confirm the "turn off" messages
                expected = new[]
                               {
                                   (byte) ((int) RequestMessageType.ReportDigitalPort | portNumber),
                                   (byte) 0x00
                               };
                actual = FirmataEncoder.BuildReportDigitalPortRequest(portNumber, false);
                actual.VerifyExpectedByValue(expected);
            }
        }

        ///<summary>
        ///  A test for BuildServoConfigRequest
        ///</summary>
        [TestMethod]
        public void BuildServoConfigRequestTest()
        {
            const int minPulse = 400;
            const int maxPulse = 1500;
            const int angle = 120;
            const int pinNumber = 6;

            var expected = new[]
                               {
                                   (byte) ResponseMessageType.SysexStart,
                                   (byte) RequestMessageType.ServoConfig,
                                   (byte) pinNumber,
                                   (byte) 0x10, // LSB min pulse
                                   (byte) 0x03, // MSB min pulse
                                   (byte) 0x5C, // LSB max pulse
                                   (byte) 0x0B, // MSB max pulse
                                   (byte) 0x78, // LSB angle
                                   (byte) 0x00, // MSB angle
                                   (byte) ResponseMessageType.SysexEnd
                               };

            var actual = FirmataEncoder.BuildServoConfigRequest(pinNumber, minPulse, maxPulse, angle);
            actual.VerifyExpectedByValue(expected);
        }

        ///<summary>
        ///  A test for BuildServoPositionRequest
        ///</summary>
        [TestMethod]
        public void BuildServoPositionRequestTest()
        {
            const int value = 180;
            for (var pinNumber = 2; pinNumber < 13; pinNumber++)
            {
                var expected = new byte[] {(byte) (0xE0 | pinNumber), 0x34, 0x01};
                var actual = FirmataEncoder.BuildServoPositionRequest(pinNumber, value);

                actual.VerifyExpectedByValue(expected);
            }
        }

        ///<summary>
        ///  Test that BuildSetPinModeRequest builds the correct message for each valid pin and mode.
        ///</summary>
        [TestMethod]
        public void BuildSetPinModeRequestTest()
        {
            for (var pinNumber = 2; pinNumber < 13; pinNumber++)
            {
                foreach (PinMode pinMode in Enum.GetValues(typeof (PinMode)))
                {
                    var expected = new[]
                                       {
                                           (byte) (RequestMessageType.SetPinMode),
                                           (byte) pinNumber,
                                           (byte) pinMode
                                       };

                    var actual = FirmataEncoder.BuildSetPinModeRequest(pinNumber, pinMode);

                    actual.VerifyExpectedByValue(expected);
                }
            }
        }

        ///<summary>
        ///  A test for BuildSetSamplingIntervalRequest
        ///</summary>
        [TestMethod]
        public void BuildSetSamplingIntervalRequestTest()
        {
            int[] testValues = {0, 0x5A, 0x400};
            var expected = new[]
                               {
                                   (byte) ResponseMessageType.SysexStart,
                                   (byte) RequestMessageType.SetSamplingInterval,
                                   (byte) 0x00,
                                   (byte) 0x00,
                                   (byte) ResponseMessageType.SysexEnd
                               };

            foreach (var value in testValues)
            {
                if (value == 90)
                {
                    expected[2] = 0x5A;
                    expected[3] = 0x00;
                }
                else if (value == 1024)
                {
                    expected[2] = 0x00;
                    expected[3] = 0x08;
                }
                else
                {
                    expected[2] = 0x00;
                    expected[3] = 0x00;
                }
                var actual = FirmataEncoder.BuildSetSamplingIntervalRequest(value);

                actual.VerifyExpectedByValue(expected);
            }
        }

        ///<summary>
        ///  A test for BuildSystemResetRequest
        ///</summary>
        [TestMethod]
        public void BuildSystemResetRequestTest()
        {
            var expected = new[]
                               {
                                   (byte) (RequestMessageType.SystemReset)
                               };
            var actual = FirmataEncoder.BuildSystemResetRequest();

            actual.VerifyExpectedByValue(expected);
        }

        #endregion
    }
}